package com.zhang.config;

import com.zhang.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro配置类
 * 三步走：
 *      1.配置shiroFilterFactoryBean
 *      2.配置安全管理器
 *      3.创建自定义realm
 */

@Configuration
public class  ShiroConfig {

	/**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，因为在
     * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
     *
     * Filter Chain定义说明 1、一个URL可以配置多个Filter，使用逗号分隔 2、当设置多个过滤器时，全部验证通过，才视为通过
     * 3、部分过滤器可指定参数，如perms，roles
     *
     */

    //1. 创建安全shiroFifter 负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 给filter设置安全管理器    （必须设置 SecurityManager)
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //设置默认认证界面认证   (如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面)
        shiroFilterFactoryBean.setLoginUrl("/login.html");

        // 拦截器.(配置系统的受限资源和公共资源)
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/drawImage", "anon");

        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");


        // <!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问（也就是不需要认证授权）-->
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    //2. 创建安全管理器
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(myRealm());
        return securityManager;
    }

    /**
     * 身份认证realm; (这个需要自己写，账号密码校验；权限等)
     * 
     * @return
     */
    @Bean
    public MyRealm myRealm() {
    	MyRealm myRealm = new MyRealm();
/*        //优化1：开启Ehcache缓存，将用户角色存储到Ehcache中
        myRealm.setCacheManager(new EhCacheManager());
        myRealm.setCachingEnabled(true);//开启全局缓存
        myRealm.setAuthenticationCachingEnabled(true);//开启认证缓存
        myRealm.setAuthorizationCacheName("authorizationCache");//起个小名
        myRealm.setAuthorizationCachingEnabled(true);//开启授权缓存
        myRealm.setAuthenticationCacheName("authenticationCache");//起个小名*/

        //优化2：开启Redis实现缓存
/*
        myRealm.setCacheManager(new RedisCacheManager());
        myRealm.setCachingEnabled(true);//开启全局缓存
        myRealm.setAuthenticationCachingEnabled(true);//开启认证缓存
        myRealm.setAuthorizationCacheName("authorizationCache");//起个小名
        myRealm.setAuthorizationCachingEnabled(true);//开启授权缓存
        myRealm.setAuthenticationCacheName("authenticationCache");//起个小名
*/

        return myRealm;
    }

    /**
     * Shiro生命周期处理器
     * 功能：用于管理Shiro内部组件的生命周期，确保它们在Spring容器正确初始化和销毁。
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

}
